﻿using ServerCommon.Log;
using ServerCommon.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ServerCommon.Net
{
    /// <summary>
    /// 数据缓存
    /// </summary>
    public class BufferMemory : IObjectItem
    {
        /// <summary>
        /// arr中有效的数据长度
        /// </summary>
        public int Len { get; private set; }

        /// <summary>
        /// arr中有效数据开始的索引
        /// </summary>
        public int Idx { get; private set; }

        /// <summary>
        /// 数据块
        /// </summary>
        private byte[] Arr { get; set; }

        /// <summary>
        /// 剩余可用空间 = 数据总长度 - （有效数据的索引+有效数据的长度）
        /// 前面空余的位置无法重复使用
        /// </summary>
        public int RemainderSpaceLen
        {
            get { return Arr.Length - Idx - Len; }
        }

        public bool ObjectValid { get; set; }

        /// <summary>
        /// 需要自己创建数据块（有可能有memory fragment的问题）
        /// </summary>
        public BufferMemory()
        {
            Len = 0;
            Idx = 0;
            Arr = null;
        }

        public bool Reset(int capactiy)
        {
            if (capactiy <= 0)
            {
                return false;
            }
            Len = 0;
            Idx = 0;
            if (Arr == null)
            {
                Arr = new byte[capactiy];
            }
            Array.Clear(Arr, 0, capactiy);
            return true;
        }

        /// <summary>
        /// 移除指定长度的数据,从前向后移除
        /// 例如，[00111111] 00是移除的数据
        /// </summary>
        /// <param name="len"></param>
        /// <returns></returns>
        public int Remove(int len)
        {
            int l = Math.Min(Len, len);
            Array.Clear(Arr, Idx, l);
            Len -= l;
            Idx += l;

            return l;
        }

        /// <summary>
        /// 读取指定长度的数据
        /// 只能从头部读取数据
        /// </summary>
        /// <param name="len"></param>
        /// <param name="dstArr"></param>
        /// <param name="dstIdx"></param>
        /// <returns></returns>
        public int Read(int len, byte[] dstArr, int dstIdx)
        {
            return Read(len, dstArr, dstIdx, false);
        }

        public int Copy(int len, byte[] dstArr, int dstIdx)
        {
            return Read(len, dstArr, dstIdx, true);
        }

        /// <summary>
        /// 写入指定长度的数据
        /// 只能从尾部写入数据
        /// </summary>
        /// <param name="len"></param>
        /// <param name="srcArr">源buffer的指针</param>
        /// <param name="srcIdx">源buffer的索引</param>
        /// <returns></returns>
        public int Write(int len, byte[] srcArr, int srcIdx)
        {
            if (len <= 0 || srcArr == null || srcArr.Length <= 0 || srcIdx < 0)
            {
                return 0;
            }

            if (len > RemainderSpaceLen)
            {
                return 0;
            }

            Buffer.BlockCopy(srcArr, srcIdx, Arr, Idx + Len, len);
            Len += len;

            return len;
        }

        /// <summary>
        /// 读取指定长度的数据
        /// </summary>
        /// <param name="len">要读取的数据长度</param>
        /// <param name="dstArr">目标buffer的指针</param>
        /// <param name="dstIdx">目标buffer的偏移（有效起始位置）</param>
        /// <param name="copy">
        /// copy true：顾名思义，只是从当前数据中复制想要copy的出去
        /// copy false:从当前数据中读取想要的部分后，当前就不再保存读过的数据</param>
        /// <returns></returns>
        private int Read(int len, byte[] dstArr, int dstIdx, bool copy)
        {
            if (len <= 0 || dstArr == null || dstArr.Length <= 0 || dstIdx < 0)
            {
                return 0;
            }

            if (len > Len || (dstArr.Length - dstIdx) < len)
            {
                return 0;
            }

            Buffer.BlockCopy(Arr, Idx, dstArr, dstIdx, len);
            if (!copy)
            {
                Idx += len;
                Len -= len;
            }
            return len;
        }

        public void Visual_Constructor()
        {
        }

        public void Visual_Destructor()
        {
        }
    }

    public class BufferProtocol
    {
        private static LogHelper log = LogHelper.GetLogger(typeof(BufferProtocol));

        private ObjectPool<BufferMemory>? _memoryPool;
        private int _blockSize;

        public int DataLen { get; private set; }
        public LinkedList<BufferMemory> ListData { get; private set; }

        public BufferProtocol(int memoryBlockSize, ObjectPool<BufferMemory> memoryPool)
        {
            _blockSize = memoryBlockSize;
            _memoryPool = memoryPool;

            DataLen = 0;
            ListData = new LinkedList<BufferMemory>();
        }

        public void Clear()
        {
            foreach (var v in ListData)
            {
                _memoryPool?.Free(v);
            }
            ListData.Clear();

            _memoryPool = null;
            _blockSize = 0;
            DataLen = 0;
        }

        public void Remove(int len)
        {
            while (len > 0)
            {
                var v = ListData?.First?.Value;
                int l = v.Remove(len);
                len -= l;
                if (v.Len <= 0)
                {
                    ListData?.RemoveFirst();
                }
                DataLen -= l;
            }
        }

        public int Write(int dataLen, byte[] srcArr, int srcIdx = 0)
        {
            if (srcArr == null || srcArr.Length <= 0)
            {
                return 0;
            }

            while (dataLen > 0)
            {
                BufferMemory? bd = null;

                if (ListData?.Count > 0)
                {
                    bd = ListData?.Last?.Value;
                }

                bool add = false;

                if (bd != null)
                {
                    if (bd.RemainderSpaceLen < dataLen)
                    {
                        bd = null;
                    }
                }

                if (bd == null)
                {
                    bd = _memoryPool?.Alloc();
                    bd?.Reset(_blockSize);

                    add = true;
                }

                int len = Math.Min(bd.RemainderSpaceLen, dataLen);
                bd.Write(len, srcArr, srcIdx);

                if (add)
                {
                    ListData.AddLast(bd);
                }

                DataLen += len;
                dataLen -= len;
                srcIdx += len;
            }

            return dataLen;
        }

        public int WriteLock(int dataLen, byte[] srcArr, int srcIdx = 0)
        {
            if (srcArr == null || srcArr.Length <= 0)
            {
                return 0;
            }

            lock (ListData)
            {
                BufferMemory bd = null;
                bool add = false;
                int len = 0;
                while (dataLen > 0)
                {
                    bd = null;

                    if (ListData?.Count > 0)
                    {
                        bd = ListData.Last.Value;
                    }

                    add = false;

                    if (bd != null)
                    {
                        if (bd.RemainderSpaceLen <= 0)
                        {
                            bd = null;
                        }
                    }

                    if (bd == null)
                    {
                        bd = _memoryPool.Alloc();
                        bd.Reset(_blockSize);

                        add = true;
                    }

                    len = Math.Min(bd.RemainderSpaceLen, dataLen);
                    bd.Write(len, srcArr, srcIdx);

                    if (add)
                    {
                        ListData?.AddLast(bd);
                    }

                    DataLen += len;
                    dataLen -= len;
                    srcIdx += len;
                }
            }

            return dataLen;
        }

        public int Copy(int dataLen, byte[] dstArr)
        {
            return Read(dataLen, dstArr, true);
        }

        public int Read(int dataLen, byte[] dstArr)
        {
            return Read(dataLen, dstArr, false);
        }

        public int ReadLock(int dataLen, byte[] dstArr)
        {
            return Read(dstArr, 0, dataLen);
        }

        public int Read(byte[] buff, int offset, int dataLen)
        {
            if (null == buff || offset < 0 || dataLen <= 0)
            {
                log.ErrorFormat("read buff == null, offset:{0} len:{1}", offset, dataLen);
                return 0;
            }

            if (buff.Length < dataLen)
            {
                log.ErrorFormat("read. bufflen:{0},offset:{1} len:{2}", buff.Length, offset, dataLen);
                return 0;
            }

            BufferMemory? bd = null;
            int readLen = 0;
            int totalRreadLen = 0;
            int dstIdx = offset;
            lock (ListData)
            {
                while (ListData.Count > 0)
                {
                    bd = ListData.First?.Value;
                    if (bd?.Len > 0)
                    {
                        readLen = Math.Min(bd.Len, dataLen);
                        totalRreadLen += bd.Read(readLen, buff, dstIdx);

                        dstIdx += readLen;
                        dataLen -= readLen;
                    }

                    if (bd?.Len <= 0)
                    {
                        ListData.RemoveFirst();
                        _memoryPool?.Free(bd);
                        bd = null;
                    }

                    if (dataLen == 0)
                    {
                        break;
                    }
                }

                DataLen -= totalRreadLen;
            }

            return totalRreadLen;
        }

        private int Read(int dataLen, byte[] dstArr, bool copy)
        {
            if (dataLen <= 0 || dstArr == null || dstArr.Length <= 0)
            {
                return 0;
            }

            if (dataLen > DataLen || dstArr.Length < dataLen)
            {
                return 0;
            }

            BufferMemory bd = null;
            int readLen = 0;
            int totalRreadLen = 0;
            int dstIdx = 0;
            int idx = 0;

            while (ListData.Count > 0)
            {
                bd = ListData.First.Value;
                if (bd.Len > 0)
                {
                    readLen = Math.Min(bd.Len, dataLen);
                    if (copy)
                    {
                        totalRreadLen += bd.Copy(readLen, dstArr, dstIdx);
                    }
                    else
                    {
                        totalRreadLen += bd.Read(readLen, dstArr, dstIdx);
                    }

                    dstIdx += readLen;
                    dataLen -= readLen;
                }

                if (bd.Len <= 0)
                {
                    ListData.RemoveFirst();
                    _memoryPool?.Free(bd);
                    bd = null;
                }
                else
                {
                    idx += 1;
                }

                if (dataLen == 0)
                {
                    break;
                }

                if (idx >= ListData.Count)
                {
                    totalRreadLen = 0;
                    break;
                }
            }

            if (!copy)
            {
                DataLen -= totalRreadLen;
            }

            return totalRreadLen;
        }
    }
}
